package com.github.florent37.shapeofview;

import ohos.agp.components.AttrHelper;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.StackLayout;
import ohos.agp.components.element.Element;
import ohos.agp.render.BlendMode;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.render.Texture;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Size;

import com.github.florent37.shapeofview.manager.ClipPathManager;

import java.util.Optional;

/**
 * ShapeOfView
 *
 * @since 2021
 */
public abstract class ShapeOfView extends StackLayout {
    //获取屏幕分辨率
    private Optional<Display> display = null;
    private final Paint clipPaint = new Paint();
    private final Path clipPath = new Path();
    protected Element drawable = null;
    private ClipPathManager clipManager = new ClipPathManager();
    private boolean requiersShapeUpdate = true;
    private PixelMapHolder pixelMapHolder;//像素图片持有者
    final Path rectView = new Path();
    private int drawColor = 0xffffffff;
    private int widthSize, heightSize;
    private DrawTask mDrawTask = new DrawTask() {

        @Override
        public void onDraw(Component component, Canvas canvas) {
            if (requiersShapeUpdate) {
                calculateLayout(widthSize, heightSize);
                requiersShapeUpdate = false;
            }
            RectFloat rect = new RectFloat();
            rect.left = 0;
            rect.top = 0;
            rect.right = getWidth();
            rect.bottom = getHeight();
            int layer = canvas.saveLayer(rect, new Paint());
            canvas.drawColor(drawColor, BlendMode.SRC);
            clipPaint.setBlendMode(BlendMode.CLEAR);
            canvas.drawPath(clipPath, clipPaint);
            clipPaint.setBlendMode(null);

            canvas.restoreToCount(layer);
        }
    };

    /**
     * setDrawColor
     *
     * @param drawColor
     */
    public void setDrawColor(int drawColor) {
        this.drawColor = drawColor;
    }

    /**
     * ShapeOfView
     *
     * @param context context
     */
    public ShapeOfView(Context context) {
        super(context);
        init(context, null);
    }

    /**
     * ShapeOfView
     *
     * @param context context
     * @param attrs attrs
     */
    public ShapeOfView(Context context, AttrSet attrs) {
        super(context, attrs);
        init(context, attrs);
        addDrawTask(mDrawTask);
    }

    /**
     * ShapeOfView
     *
     * @param context context
     * @param attrs attrs
     * @param defStyleAttr defStyleAttr
     */
    public ShapeOfView(Context context, AttrSet attrs, int defStyleAttr) {
        super(context, attrs, String.valueOf(defStyleAttr));
        init(context, attrs);
        addDrawTask(mDrawTask);
    }

    private void init(Context context, AttrSet attrs) {
        clipPaint.setAntiAlias(false);
        clipPaint.setFilterBitmap(false);
        clipPaint.setColor(Color.BLUE);
        clipPaint.setStyle(Paint.Style.FILL_STYLE);
        clipPaint.setStrokeWidth(1);
        clipPaint.setBlendMode(BlendMode.DST_IN);
        //获取屏幕密度
        display = DisplayManager.getInstance().getDefaultDisplay(context);
        setEstimateSizeListener(new EstimateSizeListener() {

            @Override
            public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
                widthSize = EstimateSpec.getSize(widthMeasureSpec);
                heightSize = EstimateSpec.getSize(heightMeasureSpec);
                return false;
            }
        });

    }

    /**
     * dpToPx
     *
     * @param dp dp
     * @return float
     */
    protected float dpToPx(float dp) {
        return dp * display.get().getAttributes().densityPixels;
    }

    /**
     * pxToDp
     *
     * @param px px
     * @return float
     */
    protected float pxToDp(float px) {
        return px / display.get().getAttributes().densityPixels;
    }

    private void calculateLayout(int width, int height) {
        rectView.reset();
        rectView.addRect(0f, 0f, 1f * getWidth(), 1f * getHeight(), Path.Direction.CLOCK_WISE);

        if (clipManager != null) {
            if (width > 0 && height > 0) {
                clipManager.setupClipLayout(width, height);

                clipPath.reset();
                clipPath.set(clipManager.createMask(width, height));
                /*//创建位图对象*/
                PixelMap.InitializationOptions initializationOptions = new PixelMap.InitializationOptions();
                initializationOptions.size = new Size(AttrHelper.vp2px(widthSize, getContext()), AttrHelper.vp2px(heightSize, getContext()));
                initializationOptions.pixelFormat = PixelFormat.ARGB_8888;
                PixelMap clipBitmap = PixelMap.create(initializationOptions);
                pixelMapHolder = new PixelMapHolder(clipBitmap);
                final Canvas canvas = new Canvas(new Texture(clipBitmap));
                if (drawable != null) {
                    drawable.setBounds(0, 0, width, height);
                    drawable.drawToCanvas(canvas);
                } else {
                    canvas.drawPath(clipPath, clipManager.getPaint());
                }
            }
        }
        invalidate();

    }

    /**
     * setDrawable
     *
     * @param element element
     */
    public void setDrawable(Element element) {
        this.drawable = drawable;
        requiresShapeUpdate();
    }

    /**
     * setClipPathCreator
     *
     * @param createClipPath createClipPath
     */
    public void setClipPathCreator(ClipPathManager.ClipPathCreator createClipPath) {
        ((ClipPathManager) clipManager).setClipPathCreator(createClipPath);
        requiresShapeUpdate();
    }

    /**
     * requiresShapeUpdate
     */
    public void requiresShapeUpdate() {
        this.requiersShapeUpdate = true;
        invalidate();
    }

}
